require 'test/helper'
include Test::Quick::Generators

class TC_Generators < Test::Unit::TestCase
  def test_int_exists
    ok IntegerGen
  end

  def test_float_exists
    ok FloatGen
  end

  def test_char_exists
    ok CharGen
  end

  def test_string_exists
    ok StringGen
  end
  # TODO add Natural, Array, Enumerable, Hash, Struct, Range, Email, Person, Text, HTML, whatever
  # XXX refactor tests with util function
  def test_natural_generates_natural
    ok NaturalGen
    ok ng = NaturalGen.new
    100.times do 
      n = ng.next
      n.is_a?(Integer) and n >=0}
    end
  end

  def test_int_generates_int
    # Shall we pass a value here?
    ok ig=IntegerGen.new
    # we use external iterator interface.
    # What other options we have? What are the advantages?
    100.times {ok ig.next.is_a?(Integer)}

    assert true
  end

  def xxxtest_int_generates_limits
    ig = IntegerGen.new
    # QuickCheck papers don't talk about this IIRC, but IMHO
    # a good test checks for boundary and extreme conditions.
    # It would be nice to know that this kind of things are tested
    # and are tested early
    ary = []
    1000.times { ary << ig.next }
    ok ary.include?(1)
    ok ary.include?(0)
    ok ary.include?(-1)
    ok ary.find?{|x| x >  2**30}
    ok ary.find?{|x| x < -2**30}
  end

  def test_char_generates_chars
    # See above note on optional value
    ok cg = CharGen.new
    100.times do
      c = cg.next
      ok c.is_a?(String) && c.size==1
    end
  end

  def test_float_generator_floats
    ok fg = FloatGen.new
    100.times {ok fg.next.is_a?(Float)}
  end

end


class TC_GeneratorsAlgebra < Test::Unit::TestCase
  # it is useful to build higher level generators out of low_level ones

  def test_compose_two
    ok pairgen = compose(IntegerGen, FloatGen)
    100.times do
      a,b = pairgen.next
      ok a.is_a?(Integer)
      ok b.is_a?(Float)
    end
  end

  def test_compose_three
    ok triplegen = compose(IntegerGen, FloatGen, CharGen)
    10.times do
      a,b,c = triplegen.next
      ok a.is_a?(Integer)
      ok b.is_a?(Float)
      ok c.is_a?(String)
      ok c.size==1
    end
  end

  def test_compose_with_proc_many
    ok triplet = compose(CharGen,CharGen,CharGen) {|a,b,c| a+b+c}
    10.times do
      t = triplet.next
      ok t.is_a?(String)
      assert_equal 3, t.size
    end
  end

  def test_compose_with_proc_one
    ok numeric_string = compose(IntegerGen) {|x| x.to_s}
    10.times do 
      ns = numeric_string.next
      ok ns.is_a?(String)
      # Integer('c') raises an exception, so
      # we know that this will fail if we didn't produce a string 
      # representing a number
      assert Integer(ns)
    end
  end

  # quickcheck has these two.
  # Shall we use classes?
  def test_two
    ok i2gen = two(Integer)
    pair = i2gen.next
    assert_equal 2, pair.size
    pair.all? {|x| ok x.is_a?(Integer)}
  end

  def test_three
    ok f3gen = three(Float)
    triple = f3gen.next
    assert_equal 3,triple.size
    triple.all? {|x| ok x.is_a?(Float)}
  end

  def test_vector
    ok f5gen = vector(Float,5)
    vec = f5gen.next
    assert_equal 5, vec.size
    vec.all? {|x| ok x.is_a?(Float)}
  end
end
